package com.api.utils;

import com.api.dispose.inter.PluginInterface;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashMap;
import java.util.Map;

/**
 * 插件工具类
 * 运行时，需要将插件jar包放到 /serverless/plugins/ 目录下才可运行,同样赋予相应的权限
 * windows 系统下，插件文件路径为：c:/serverless/plugins/pluginName.jar
 * linux 系统下，插件文件路径为：/serverless/plugins/pluginName.jar
 * 插件需要包含一个接口 PluginInterface  pom.xml中引入common-basic-dispose依赖
 *
 *
 * @author 李大牛家的牛 (scafel@163.com)
 * @createDate 2025/5/20-15:16
 * @description ToDo...
 **/
@Slf4j
public class PluginUtils {

    /**
     * 验证插件文件及文件夹是否存在
     * windows 系统下，插件文件路径为：d:/serverless/plugins/pluginName.jar
     * linux 系统下，插件文件路径为：/serverless/plugins/pluginName.jar
     *
     * @param pluginName 插件名称
     * @return 插件文件路径
     */
    private static String checkPluginFile(String pluginName) {
        // 参数校验
        if (pluginName == null || pluginName.isEmpty() || !pluginName.matches("^[a-zA-Z0-9_-]+$")) {
            log.error("插件名称不能为空或包含非法字符，仅允许字母、数字、下划线和连字符");
            return null;
        }
        try {
            String pluginPath = "";
            if (FileUtils.isWindows()) {
                pluginPath = "d:/serverless/plugins/" + pluginName + ".jar";
            } else {
                pluginPath = "/serverless/plugins/" + pluginName + ".jar";
            }
            if (FileUtils.exist(pluginPath)) {
                log.info("插件文件存在");
                return pluginPath;
            }
        } catch (Exception ex) {
            log.error("插件文件不存在|" + ex.getMessage());
        }
        return null;
    }

    /**
     * 运行插件
     * @param pluginName 插件名称
     * @param funName 插件方法名
     * @param args 插件参数
     * @return 插件执行结果
     */
    public static Object invokePlugin(Object app, String pluginName, String pluginClassName, String funName, Map<String, String> args) {
        // 参数校验
        if (app == null) {
            throw new IllegalArgumentException("应用上下文不能为空");
        }
        if (pluginName == null || pluginName.isEmpty() || StrUtils.isBlank(pluginName)) {
            throw new IllegalArgumentException("插件名称不能为空");
        }
        if (pluginClassName == null || pluginClassName.isEmpty() || StrUtils.isBlank(pluginClassName)) {
            throw new IllegalArgumentException("插件类名不能为空");
        }
        if (funName == null || funName.isEmpty() || StrUtils.isBlank(funName)) {
            throw new IllegalArgumentException("插件方法名不能为空");
        }
        if (args == null) {
            args = new HashMap<>(); // 避免空指针
        }
        String pluginPath = checkPluginFile(pluginName);
        if (pluginPath == null) {
            log.error("插件文件不存在，插件名称: {}", pluginName);
            throw new RuntimeException("插件文件不存在");
        }
        try (URLClassLoader classLoader = createClassLoader(pluginPath)) { // 使用 try-with-resources 确保资源关闭
            // 加载插件类
            Class<?> pluginClass = classLoader.loadClass(pluginClassName);

            if (pluginClass.isInterface()) {
                throw new RuntimeException("插件类不能为接口");
            }
            if (pluginClass.getConstructors().length == 0) {
                throw new RuntimeException("插件类必须包含默认构造函数");
            }

            PluginInterface plugin = (PluginInterface) pluginClass.getConstructor().newInstance();
            plugin.setApplicationContext(app);

            // 执行插件方法
            Object result;
            if (args.isEmpty()) {
                result = plugin.execute(funName);
            } else {
                result = plugin.execute(funName, args);
            }
            log.info("插件 {} 执行类 {} 执行方法 {} 结果：{}", pluginName, pluginClassName, funName, result);
            return result;
        } catch (ClassNotFoundException e) {
            log.error("插件类 {} 未找到，插件名称: {}, 错误信息: {}", pluginClassName, pluginName, e.getMessage(), e);
            throw new RuntimeException("插件类未找到", e);
        } catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
            log.error("插件类 {} 初始化失败，插件名称: {}, 错误信息: {}", pluginClassName, pluginName, e.getMessage(), e);
            throw new RuntimeException("插件类初始化失败", e);
        } catch (ClassCastException e) {
            log.error("插件类 {} 未实现 PluginInterface 接口，插件名称: {}", pluginClassName, pluginName, e);
            throw new RuntimeException("插件类未实现 PluginInterface 接口", e);
        } catch (IOException e) {
            log.error("插件文件路径错误，插件名称: {}, 错误信息: {}", pluginName, e.getMessage(), e);
            throw new RuntimeException("插件文件路径错误", e);
        }
    }

    /**
     * 运行插件（无参数）
     * @param pluginName 插件名称
     * @param funName 插件方法名
     * @return 插件执行结果
     */
    public Object invokePlugin(Object app, String pluginName, String pluginClassName, String funName) {
        return invokePlugin(app, pluginName, pluginClassName, funName, null);
    }

    /**
     * 创建 URLClassLoader 实例
     * @param pluginPath 插件文件路径
     * @return URLClassLoader 实例
     * @throws MalformedURLException 如果路径无效
     */
    private static URLClassLoader createClassLoader(String pluginPath) throws MalformedURLException {
        File pluginFile = new File(pluginPath);
        if (!pluginFile.exists() || !pluginFile.isFile()) {
            throw new RuntimeException("插件文件无效");
        }
        return new URLClassLoader(new URL[]{pluginFile.toURI().toURL()}, Thread.currentThread().getContextClassLoader());
    }

}
